﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using System.Windows.Threading;
using HelixToolkit.Wpf;

namespace GameBoard
{
    [DataContract]
    public class VisualsViewModel : INotifyPropertyChanged
    {
        public enum TroopFormation
        {
            Grid,
            Circle,
            Random,
            Tight
        }

        private const int LineDiameter = 5;
        private static VisualsViewModel _instance;
        private static readonly Random _random = new Random();

        [DataMember] private Dictionary<Guid, MoveablePicture> _charactersToMoveablePicture =
            new Dictionary<Guid, MoveablePicture>();

        private List<GameBoardVisual> _gameBoardVisuals = new List<GameBoardVisual>();
        private List<MovementCircleVisualFactory> _groupMovementCircles = new List<MovementCircleVisualFactory>();
        private ProjectionCamera _mainCamera;
        private bool _orientAllCamerasToMatchMain = true;
        private bool _tapeMeasurerActive;
        private TapeMeasurerControl _tapeMeasurerControl = new TapeMeasurerControl();

        private Dictionary<DispatcherTimer, Guid> _temporaryVisuals =
            new Dictionary<DispatcherTimer, Guid>();

        [DataMember] private TerrainViewModel _terrainViewModel = new TerrainViewModel();
        private GridVisualFactory _visualGridFactory;
        private Color defaultColor = Colors.Gray;

        private VisualsViewModel()
        {
            Initialize();
        }

        public static VisualsViewModel Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new VisualsViewModel();
                }
                return _instance;
            }
            private set { _instance = value; }
        }

        public bool MultiplesAreSelected
        {
            get
            {
                return _charactersToMoveablePicture.
                    Values.Count(x => x.IsSelected) > 1;
            }
        }

        [DataMember]
        public BoardInfo CurrentBoardInfo { get; set; }

        public List<MovementCircleVisualFactory> GroupMovementCircles
        {
            get { return _groupMovementCircles; }
            set { _groupMovementCircles = value; }
        }

        public GridVisualFactory VisualGrid
        {
            get { return _visualGridFactory; }
            set { _visualGridFactory = value; }
        }

        public bool TapeMeasurerActive
        {
            get { return _tapeMeasurerActive; }
            set
            {
                _tapeMeasurerActive = value;
                if (!value)
                {
                    _tapeMeasurerControl.CleanupTapeMeasurerVisuals();
                }
            }
        }

        public Dictionary<Guid, MoveablePicture> CharactersToMoveablePicture
        {
            get { return _charactersToMoveablePicture; }
            set { _charactersToMoveablePicture = value; }
        }

        public int NumberActive
        {
            get { return _charactersToMoveablePicture.Count(x => x.Value.IsSelected); }
        }

        public bool LockRotation
        {
            set
            {
                foreach (GameBoardVisual cur in _gameBoardVisuals)
                {
                    cur.LockRotation = value;
                }
            }
        }

        public ProjectionCamera MainCamera
        {
            get { return _mainCamera; }
            set
            {
                if (_mainCamera != null)
                {
                    _mainCamera.Changed -= MainCameraViewChanged;
                }
                _mainCamera = value;
                if (_mainCamera != null)
                {
                    _mainCamera.Changed += MainCameraViewChanged;
                }
            }
        }

        public bool OrientAllCamerasToMatchMain
        {
            get { return _orientAllCamerasToMatchMain; }
            set { _orientAllCamerasToMatchMain = value; }
        }

        public TerrainViewModel TerrainViewModel
        {
            get { return _terrainViewModel; }
        }


        public void SetInitialBackground()
        {
            string[] files = Directory.GetFiles(GameboardConstants.MapPictureDirectory);
            if (files.Any())
            {
                SetBoardBackground(files.First(), 0, 0, 0, true, null);
            }
            else
            {
                SetBoardToDefault();
            }
            //var tower = MakeModelVisual3DFromObjectFile(@"C:\GuardTower\GuardTower.obj", Materials.Gray, Materials.Brown);
            //var rotate = new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(1, 0, 0), 90), new Point3D(0, 0, 0));
            //tower.Transform = rotate;

            //_gameBoardVisual.AddVisual(tower);
        }


        [OnDeserialized]
        private void OnDeserialized(StreamingContext sc)
        {
            Initialize();
            _tapeMeasurerControl = new TapeMeasurerControl();
        }

        private void Initialize()
        {
            _temporaryVisuals = new Dictionary<DispatcherTimer, Guid>();
            _groupMovementCircles = new List<MovementCircleVisualFactory>();
            _gameBoardVisuals = new List<GameBoardVisual>();
        }

        public MoveablePicture AddImagePieceToMap(String charImageFile, Color pieceColor, String name, double height,
            Point3D location, List<StatusEffectDisplay> statusEffects, double speed, Guid characterGuid,
            List<int> movementCircleRadiusList)
        {
            double heightFeet = height;
            while (CharactersToMoveablePicture.Values.Any(
                existingImage =>
                    existingImage.Location.X == location.X &&
                    existingImage.Location.Y == location.Y &&
                    existingImage.Location.Z == location.Z))
            {
                location = new Point3D(location.X + heightFeet, location.Y, location.Z);
            }
            var charImage = new MoveablePicture(charImageFile,
                heightFeet,
                name,
                pieceColor,
                location,
                statusEffects,
                speed,
                movementCircleRadiusList,
                characterGuid
                );
            RegisterMoveablePicture(charImage, characterGuid);
            return charImage;
        }

        private void RegisterMoveablePicture(MoveablePicture charImage, Guid characterGuid)
        {
            if (_charactersToMoveablePicture.ContainsKey(characterGuid))
            {
                _charactersToMoveablePicture.Remove(characterGuid);
            }
            _charactersToMoveablePicture.Add(characterGuid, charImage);
            foreach (GameBoardVisual cur in _gameBoardVisuals)
            {
                cur.AddVisual(charImage.TheCharImageFactory.FactoryVisualID, charImage.TheCharImageFactory.MakeVisual());
                cur.AddVisual(charImage.TheBaseConeFactory.FactoryVisualID, charImage.TheBaseConeFactory.MakeVisual());
            }
        }

        public void DrawAttack(Guid attackerID, Guid targetID, Color materialColor,
            Duration showDuration)
        {
            MoveablePicture attacker = _charactersToMoveablePicture[attackerID];
            MoveablePicture target = _charactersToMoveablePicture[targetID];


            var data = new ArrowVisualData
            {
                Origin = attacker.Location,
                Point2 = target.Location,
                MaterialColor = materialColor,
                Diameter = 1,
                ShowDuration = showDuration,
            };
            var arrowFactory = new ArrowVisualFactory(data);
            foreach (GameBoardVisual cur in _gameBoardVisuals)
            {
                cur.AddVisual(arrowFactory.FactoryVisualID, arrowFactory.MakeVisual());
            }
            MarkVisualAsTemporary(arrowFactory.FactoryVisualID, showDuration.TimeSpan);
        }

        private void MarkVisualAsTemporary(Guid guid, TimeSpan timespan)
        {
            var timer = new DispatcherTimer();
            timer.Tick += removeTemporaryVisual;
            timer.Interval = timespan;
            timer.Start();
            _temporaryVisuals.Add(timer, guid);
        }

        private void removeTemporaryVisual(object sender, EventArgs e)
        {
            var timer = sender as DispatcherTimer;
            Guid matchingID = _temporaryVisuals[timer];
            foreach (GameBoardVisual cur in _gameBoardVisuals)
            {
                cur.RemoveVisual(matchingID);
            }

            timer.Stop();
        }


        public event EventHandler PieceSelected;

        protected virtual void OnPieceSelected(PieceSelectedEventArgs e)
        {
            EventHandler handler = PieceSelected;
            if (handler != null)
            {
                handler(this, e);
            }
        }


        public event EventHandler ClearSelectedPieces;

        protected virtual void OnClearSelectedPieces(ClearSelectedPiecesEventArgs e)
        {
            EventHandler handler = ClearSelectedPieces;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        public event EventHandler PieceMoved;

        protected virtual void OnPieceMoved(PieceMovedEventsArg e)
        {
            EventHandler handler = PieceMoved;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        public void ToggleSelectCharacterFromVisual(IVisual lastHit)
        {
            Guid characterID =
                _charactersToMoveablePicture.First
                    (x => x.Value.HasVisualWithID(lastHit.FactoryID)).Key;
            if (_charactersToMoveablePicture[characterID].IsSelected)
            {
                List<Guid> previousSelections =
                    CharactersToMoveablePicture.Where(x => x.Value.IsSelected).Select(z => z.Key).ToList();
                ClearSelectedCharacters();
                previousSelections.Remove(characterID);
                foreach (Guid cur in previousSelections)
                {
                    SetActive(cur);
                }
            }
            else
            {
                SetActive(characterID);
            }
        }

        private Guid IDFromPicture(MoveablePicture moveablePicture)
        {
            foreach (var cur in _charactersToMoveablePicture)
            {
                if (cur.Value == moveablePicture)
                {
                    return cur.Key;
                }
            }
            throw new Exception("Unregistered picture ID");
        }

        public void SetActive(Guid targetId)
        {
            MoveablePicture moveablePicture = _charactersToMoveablePicture[targetId];
            if (moveablePicture.IsSelected)
            {

            }
            else
            {
                moveablePicture.IsSelected = true;

                foreach (GameBoardVisual cur in _gameBoardVisuals)
                {
                    foreach (Guid curID in moveablePicture.ActivateableIDs)
                    {
                        cur.ActivateVisuals(curID);
                    }
                }
                foreach (GameBoardVisual curBoard in _gameBoardVisuals)
                {
                    if (moveablePicture.BoardsInvisibleTo.Contains(curBoard.BoardType))
                    {
                        continue;
                    }
                    foreach (MovementCircleVisualFactory cur in moveablePicture.MovementCirclesFactories)
                    {
                        curBoard.AddVisual(cur.FactoryVisualID, cur.MakeVisual());
                    }
                    curBoard.AddVisual(moveablePicture.TheInfoTextFactory.FactoryVisualID,
                                       moveablePicture.TheInfoTextFactory.MakeVisual());
                }
                OnPieceSelected(new PieceSelectedEventArgs(targetId));
            }
            _terrainViewModel.SelectedBrushChanged();
        }

        public void SetInactive(Guid targetID)
        {
            MoveablePicture moveablePicture = _charactersToMoveablePicture[targetID];
            ResetGroupMovementCircles();
            moveablePicture.IsSelected = false;
            foreach (GameBoardVisual curBoard in _gameBoardVisuals)
            {
                foreach (MovementCircleVisualFactory cur in moveablePicture.MovementCirclesFactories)
                {
                    curBoard.RemoveVisual(cur.FactoryVisualID);
                }
                curBoard.RemoveVisual(moveablePicture.TheInfoTextFactory.FactoryVisualID);
                curBoard.DeactivateVisuals(moveablePicture.TheBaseConeFactory.FactoryVisualID);
            }
        }

        public void RemovePiece(Guid uniqueID)
        {
            MoveablePicture match = _charactersToMoveablePicture[uniqueID];
            foreach (GameBoardVisual curBoard in _gameBoardVisuals)
            {
                foreach (MovementCircleVisualFactory cur in match.MovementCirclesFactories)
                {
                    curBoard.RemoveVisual(cur.FactoryVisualID);
                }
                curBoard.RemoveVisual(match.TheInfoTextFactory.FactoryVisualID);
                curBoard.RemoveVisual(match.TheBaseConeFactory.FactoryVisualID);
                curBoard.RemoveVisual(match.TheCharImageFactory.FactoryVisualID);
            }
            CharactersToMoveablePicture.Remove(uniqueID);
            match = null;
        }


        internal void SetBoardBackground(string newImage,
            double definedBoardHeight, 
            double definedBoardWidth, 
            double hillHeight,
            bool maintainRatio, byte[] hillNoise)
        {
            CurrentBoardInfo = new BoardInfo(newImage,
                definedBoardHeight, 
                definedBoardWidth, 
                hillHeight,
                maintainRatio, 
                hillNoise);

            if (TerrainViewModel.TheMapFactory != null)
            {
                RemoveVisuals(TerrainViewModel.TheMapFactory.FactoryVisualID);
            }

            Material frontMaterial;
            try
            {
                ImageBrush frontBrush = MaterialMaker.MakeImageMaterial(newImage);
                frontMaterial = new DiffuseMaterial(frontBrush);

                if (maintainRatio)
                {
                    if (definedBoardHeight <= 0)
                    {
                        CurrentBoardInfo.DefinedBoardHeight = frontBrush.ImageSource.Height/4;
                        CurrentBoardInfo.DefinedBoardWidth = frontBrush.ImageSource.Width/4;
                    }
                    else
                    {
                        CurrentBoardInfo.DefinedBoardHeight = definedBoardHeight;
                        double normalHeight = frontBrush.ImageSource.Height;
                        double normalWidth = frontBrush.ImageSource.Width;
                        double ratio = normalHeight/normalWidth;
                        CurrentBoardInfo.DefinedBoardWidth = (definedBoardHeight/ratio);
                    }
                }
                else
                {
                    CurrentBoardInfo.DefinedBoardHeight = definedBoardHeight;
                    CurrentBoardInfo.DefinedBoardWidth = definedBoardWidth;
                }
            }
            catch (Exception)
            {
                MessageBox.Show("Unable to set map background to " + newImage);
                SetBoardToDefault();
                return;
            }
            Material backMaterial = MaterialMaker.PaperbackMaterial();

            var data = new MapVisualData(CurrentBoardInfo, frontMaterial, backMaterial);
            TerrainViewModel.TheMapFactory = new MapVisualFactory(data);
            foreach (GameBoardVisual curboard in _gameBoardVisuals)
            {
                curboard.AddVisual(TerrainViewModel.TheMapFactory.FactoryVisualID, TerrainViewModel.TheMapFactory.MakeVisual());
            }
        }

        private void SetBoardToDefault()
        {
            Material frontMaterial = MaterialMaker.DefaultFrontMaterial;
            Material backMaterial = MaterialMaker.PaperbackMaterial();
            CurrentBoardInfo = new BoardInfo("", 300, 300, 0, true, null);
            MapVisualData data = new MapVisualData(CurrentBoardInfo, frontMaterial, backMaterial);
            _terrainViewModel.TheMapFactory = new MapVisualFactory(data);
            foreach (var curboard in _gameBoardVisuals)
            {
                curboard.AddVisual(TerrainViewModel.TheMapFactory.FactoryVisualID, TerrainViewModel.TheMapFactory.MakeVisual());
            }
        }

        public void MovePieceToPoint(Guid selectedID, Point3D targetPoint, TimeSpan duration, bool findGroundPoint = false)
        {
            MoveablePicture matchingVisual = _charactersToMoveablePicture[selectedID];
            var dest = new Point3D(targetPoint.X, targetPoint.Y, targetPoint.Z);
            if (findGroundPoint)
            {
                dest = _terrainViewModel.FindGroundPoint(
                    dest, _terrainViewModel, matchingVisual.LongestPictureSide, _gameBoardVisuals.First());
            }
            dest.Z += matchingVisual.FlightHeight;
            matchingVisual.SetLocationTo(dest);
            foreach (GameBoardVisual curboardVisual in _gameBoardVisuals)
            {
                curboardVisual.MoveVisual(matchingVisual.TheBaseConeFactory.FactoryVisualID, dest, duration);
                curboardVisual.MoveVisual(matchingVisual.TheCharImageFactory.FactoryVisualID, dest, duration);
                curboardVisual.MoveVisual(matchingVisual.TheInfoTextFactory.FactoryVisualID, dest, duration);
            }

            OnPieceMoved(new PieceMovedEventsArg(selectedID, targetPoint));
        }

        internal void MoveSelectedPiecesTo(Point3D point3D)
        {
            IEnumerable<KeyValuePair<Guid, MoveablePicture>> selectedCharacters =
                CharactersToMoveablePicture.Where(x => x.Value.IsSelected);
            if (selectedCharacters.Count() == 1)
            {
                MoveablePicture fc = selectedCharacters.First().Value;
                var targetPoint = new Point3D(point3D.X, point3D.Y, point3D.Z);
                MovePieceToPoint(fc.UniqueID, targetPoint, TimeSpan.FromSeconds(1));
            }
            else
            {
                //Move in formation to new location with respect to common centerpoint.
                Point3D middleSelection = Helper3DCalcs.FindMidpoint(selectedCharacters.Select(x => x.Value.Location));
                foreach (var cur in selectedCharacters)
                {
                    Point3D curP = cur.Value.Location;
                    Vector3D vectorToOldMidpoint = curP - middleSelection;
                    vectorToOldMidpoint.Normalize();
                    if (double.IsNaN(vectorToOldMidpoint.X)
                        ||
                        double.IsNaN(vectorToOldMidpoint.Y)
                        || double.IsNaN(vectorToOldMidpoint.Z))
                    {
                        MovePieceToPoint(
                            cur.Value.UniqueID, new Point3D(point3D.X, point3D.Y, point3D.Z), TimeSpan.FromSeconds(1), true);
                        continue;
                    }
                    double distance = Helper3DCalcs.DistanceBetween(curP, middleSelection);
                    vectorToOldMidpoint = vectorToOldMidpoint*distance;
                    var newPoint = new Point3D(vectorToOldMidpoint.X + point3D.X,
                        vectorToOldMidpoint.Y + point3D.Y,
                        vectorToOldMidpoint.Z + point3D.Z);
                    MovePieceToPoint(cur.Value.UniqueID, newPoint, TimeSpan.FromSeconds(1), true);
                }
            }
        }


        internal void ClearSelectedCharacters()
        {
            foreach (var cur in CharactersToMoveablePicture.Where(x => x.Value.IsSelected))
            {
                SetInactive(cur.Key);
            }
            OnClearSelectedPieces(new ClearSelectedPiecesEventArgs());
        }

        public void DrawGroupMovementCircleIfNeeded()
        {
            ResetGroupMovementCircles();
            List<Point3D> selectedPictures = CharactersToMoveablePicture.Where
                (x => x.Value.IsSelected).Select(z => z.Value.Location).ToList();
            if (selectedPictures.Count > 1)
            {
                double minSpeed = CharactersToMoveablePicture.Where(x=>x.Value.IsSelected).Min(x => 
                    x.Value.Speed);
                Point3D midPoint = Helper3DCalcs.FindMidpoint(selectedPictures);
                List<int> movementCircleRadiusList =
                    CharactersToMoveablePicture.FirstOrDefault(x => x.Value.IsSelected).Value.MovementCirclesRadiusList;
                foreach (int curCircleSize in movementCircleRadiusList)
                {
                    var g = new MovementCircleVisualFactory(
                        new MovementCircleVisualData
                        {
                            Speed = minSpeed,
                            CurCircleSize = curCircleSize,
                            Location = midPoint,
                            MaterialColor = Colors.DarkBlue
                        });
                    GroupMovementCircles.Add(g);
                    foreach (GameBoardVisual cur in _gameBoardVisuals)
                    {
                        cur.AddVisual(g.FactoryVisualID, g.MakeVisual());
                    }
                }
                foreach (var cur in CharactersToMoveablePicture.Where(x => x.Value.IsSelected))
                {
                    foreach (MovementCircleVisualFactory curMovementCircle in cur.Value.MovementCirclesFactories)
                    {
                        foreach (GameBoardVisual curboard in _gameBoardVisuals)
                        {
                            curboard.RemoveVisual(curMovementCircle.FactoryVisualID);
                        }
                    }
                }
            }
        }

        private void ResetGroupMovementCircles()
        {
            foreach (MovementCircleVisualFactory cur in GroupMovementCircles)
            {
                foreach (GameBoardVisual curboard in _gameBoardVisuals)
                {
                    curboard.RemoveVisual(cur.FactoryVisualID);
                }
            }
            GroupMovementCircles.Clear();
        }


        private void SelectFromInsideShape(Point3D point1, Point3D point2)
        {
            var selectedByShape = new List<MoveablePicture>();
            ClearSelectedCharacters();
            foreach (var cur in CharactersToMoveablePicture)
            {
                switch (_terrainViewModel.ShapePlacementType)
                {
                    case TerrainViewModel.ShapeMode.Sphere:
                        double dist = Helper3DCalcs.DistanceBetween(cur.Value.Location, point1);
                        if (dist <= _terrainViewModel.ShapeSizeValue)
                        {
                            selectedByShape.Add(cur.Value);
                        }
                        break;
                    case TerrainViewModel.ShapeMode.Cone:
                        //public static Boolean IsPointInCone(Point3D testPoint, Point3D coneApex, Point3D coneBaseCenter,
                        //            double aperture)
                        //
                        //     aperture angle
                        //       | \
                        //    H  |  \
                        //       |___\
                        //         R
                        Point3D coneBaseCenter = Helper3DCalcs.MovePointTowards(point1, point2,
                            _terrainViewModel.ShapeSizeValue);
                        double coneHeight = Helper3DCalcs.DistanceBetween(point1, coneBaseCenter);
                        double angle = Math.Atan(1);
                        if (Helper3DCalcs.IsPointInCone(cur.Value.Location, point1, coneBaseCenter, angle*2))
                        {
                            selectedByShape.Add(cur.Value);
                        }
                        break;
                    case TerrainViewModel.ShapeMode.Line:
                        Vector3D testpt = cur.Value.Location.ToVector3D();
                        Vector3D pt1 = point1.ToVector3D();
                        Vector3D pt2 = point2.ToVector3D();
                        double lengthsq = Math.Pow(Helper3DCalcs.DistanceBetween(point1, point2), 2);
                        if (Helper3DCalcs.PointIsInsideCylinder(testpt, pt1, pt2, LineDiameter, lengthsq))
                        {
                            selectedByShape.Add(cur.Value);
                        }
                        break;

                    case TerrainViewModel.ShapeMode.Square:
                        break;
                }
            }
            foreach (MoveablePicture cur in selectedByShape)
            {
                //public void SetActive(MoveablePicture moveablePicture, Color pieceColor, bool drawSingleSelectionDetails, double curSpeed, List<StatusEffectDisplay> statuses)
                SetActive(IDFromPicture(cur));
            }

            DrawGroupMovementCircleIfNeeded();
        }

        public void ClearShapePreview()
        {
            if (_previewShapeFactory == null)
                return;
            RemoveVisuals(_previewShapeFactory.FactoryVisualID);
            _previewShapeFactory = null;
        }

        private BaseVisualFactory _previewShapeFactory = null;
        private double _arrowKeyDistance = 5;

        internal void DrawSphere(Point3D point3D, bool previewShape)
        {
            
            var shapeData = new SphereVisualData
            {
                Center = point3D,
                Radius = _terrainViewModel.ShapeSizeValue,
                MaterialFile = _terrainViewModel.ObjectPlacementImageFile,
                MaterialColor = SelectedTeamColor,
                IsNotSolid = _terrainViewModel.ShapeIsTransparent,
                IsTransparent = _terrainViewModel.ShapeIsTransparent
            };
            var sp = new SphereVisualFactory(shapeData);
            if (previewShape)
            {
                _previewShapeFactory = sp;
                shapeData.IsNotSolid = true;
                shapeData.IsTransparent = true;
            }
            foreach (GameBoardVisual cur in _gameBoardVisuals)
            {
                cur.AddVisual(sp.FactoryVisualID, sp.MakeVisual());
            }
            if (!previewShape)
            {
                if (!_terrainViewModel.ShapeIsPermanent)
                {
                    MarkVisualAsTemporary(sp.FactoryVisualID, TimeSpan.FromSeconds(3));
                    SelectFromInsideShape(point3D, point3D);
                }
                else
                {
                    _terrainViewModel.RegisterFactory(sp);
                }
            }
        }

        internal void DrawCone(Point3D point1, Point3D point2, bool previewShape)
        {
            Vector3D dir = point2 - point1;
            dir.Normalize();
            var shape = new ConeVisualData
            {
                MaterialFile = _terrainViewModel.ObjectPlacementImageFile,
                MaterialColor = SelectedTeamColor,
                BaseRadius = 2,
                Height = _terrainViewModel.ShapeSizeValue,
                TopRadius = _terrainViewModel.ShapeSizeValue,
                Origin = point1,
                Normal = dir,
                IsNotSolid = _terrainViewModel.ShapeIsTransparent,
                IsTransparent = _terrainViewModel.ShapeIsTransparent
            };
            var cvf = new ConeVisualFactory(shape);
            if (previewShape)
            {
                _previewShapeFactory = cvf;
                shape.IsNotSolid = true;
                shape.IsTransparent = true;
            }
            foreach (GameBoardVisual cur in _gameBoardVisuals)
            {
                cur.AddVisual(cvf.FactoryVisualID, cvf.MakeVisual());
            }
            if (!previewShape)
            {
                if (!_terrainViewModel.ShapeIsPermanent)
                {
                    MarkVisualAsTemporary(cvf.FactoryVisualID, TimeSpan.FromSeconds(3));
                    SelectFromInsideShape(point1, point2);
                }
                else
                {
                    _terrainViewModel.RegisterFactory(cvf);
                }
            }
        }

        internal void DrawCube(Point3D point1, Point3D point3D, bool previewShape)
        {
            var a = point1;
            a.Z = 0;
            var b = point3D;
            b.Z = 0;
            var c = new Point3D(0, 35000, 0);
            var xDiff = b.X - a.X;
            var yDiff = b.Y - a.Y;
            var alpha = Math.Atan2(yDiff, xDiff) * 180.0 / Math.PI;
            if (alpha < 0)
            {
                alpha = alpha + 360;
            }


            var shape = new CubeVisualData
            {
                MaterialFile = _terrainViewModel.ObjectPlacementImageFile,
                MaterialColor = SelectedTeamColor,
                Height = _terrainViewModel.ShapeHeightValue,
                Width = _terrainViewModel.ShapeWidthValue,
                Length = _terrainViewModel.ShapeSizeValue,
                Angle = alpha,
                Center = new Point3D(point1.X, point1.Y, point1.Z + (_terrainViewModel.ShapeHeightValue/2)),
                IsNotSolid = _terrainViewModel.ShapeIsTransparent,
                IsTransparent = _terrainViewModel.ShapeIsTransparent
            };
            var cvf = new CubeVisualFactory(shape);
            if (previewShape)
            {
                _previewShapeFactory = cvf;
                shape.IsNotSolid = true;
                shape.IsTransparent = true;
            }
            foreach (GameBoardVisual cur in _gameBoardVisuals)
            {
                cur.AddVisual(cvf.FactoryVisualID, cvf.MakeVisual());
            }
            if (!previewShape)
            {
                if (!_terrainViewModel.ShapeIsPermanent)
                {
                    MarkVisualAsTemporary(cvf.FactoryVisualID, TimeSpan.FromSeconds(3));
                    SelectFromInsideShape(point3D, point3D);
                }
                else
                {
                    _terrainViewModel.RegisterFactory(cvf);
                }
            }
        }

        internal void DrawLine(Point3D point1, Point3D point2, bool previewShape)
        {
            Point3D secondPoint = Helper3DCalcs.MovePointTowards(point1, point2,
                _terrainViewModel.ShapeSizeValue);
            var pathinfo = new Point3DCollection(new List<Point3D> {point1, secondPoint});
            var shape = new TubeVisualData
            {
                MaterialFile = _terrainViewModel.ObjectPlacementImageFile,
                MaterialColor = SelectedTeamColor,
                Path = pathinfo,
                Diameter = LineDiameter,
                IsNotSolid = _terrainViewModel.ShapeIsTransparent,
                IsTransparent = _terrainViewModel.ShapeIsTransparent,
            };
            var cvf = new TubeVisualFactory(shape);
            if (previewShape)
            {
                _previewShapeFactory = cvf;
                shape.IsNotSolid = true;
                shape.IsTransparent = true;
            }
            foreach (GameBoardVisual cur in _gameBoardVisuals)
            {
                cur.AddVisual(cvf.FactoryVisualID, cvf.MakeVisual());
            }
            if (previewShape)
            {
                return;
            }
            if (!previewShape)
            {
                if (!_terrainViewModel.ShapeIsPermanent)
                {
                    MarkVisualAsTemporary(cvf.FactoryVisualID, TimeSpan.FromSeconds(3));
                    SelectFromInsideShape(point1, point2);
                }
                else
                {
                    _terrainViewModel.RegisterFactory(cvf);
                }
            }
        }

        internal void DrawObjectFile(Point3D point3D, bool previewShape)
        {
            var data = ReadFromXML(_terrainViewModel.CurrentObjectFile,
                typeof (ObjectVisualData)) as ObjectVisualData;
            data.ObjectCenter = point3D;
            var objfact = new ObjectVisualFactory(data);
            if (previewShape)
            {
                _previewShapeFactory = objfact;
                data.IsNotSolid = true;
                data.IsTransparent = true;
            }
            var objData = (objfact.VisualData) as ObjectVisualData;
            if (!File.Exists(objData.ObjectFile))
            {
                MessageBox.Show("Cannot place visual. File " + objData.ObjectFile + " did not exist.");
                return;
            }

            foreach (GameBoardVisual cur in _gameBoardVisuals)
            {
                cur.AddVisual(objfact.FactoryVisualID, objfact.MakeVisual());
            }
            if (!previewShape)
            {
                _terrainViewModel.RegisterFactory(objfact);
            }
        }


        public void DrawGrid(int squareSizes)
        {
            var data = new GridVisualData
            {
                Length = CurrentBoardInfo.DefinedBoardWidth,
                Width = CurrentBoardInfo.DefinedBoardHeight,
                Thickness = .3,
                MajorDistance = squareSizes,
                MinorDistance = squareSizes,
                Center = new Point3D(CurrentBoardInfo.DefinedBoardWidth / 2, CurrentBoardInfo.DefinedBoardHeight / 2, 1),
                MaterialColor = Colors.Black,
            };
            if (_visualGridFactory == null)
            {
                _visualGridFactory = new GridVisualFactory(data);
            }
            foreach (GameBoardVisual cur in _gameBoardVisuals)
            {
                cur.AddVisual(_visualGridFactory.FactoryVisualID, _visualGridFactory.MakeVisual());
            }
        }

        private Object ReadFromXML(string p0, Type theType)
        {
            using (var reader = new FileStream(p0, FileMode.Open, FileAccess.Read))
            {
                var ser = new DataContractSerializer(theType);
                return ser.ReadObject(reader);
            }
        }


        public Color SelectedTeamColor
        {
            get
            {
                KeyValuePair<Guid, MoveablePicture> selectedCharacter =
                    CharactersToMoveablePicture.FirstOrDefault(cur => cur.Value.IsSelected);
                if (selectedCharacter.Value != null)
                    return selectedCharacter.Value.PieceColor;
                return Colors.Red;
            }
        }

        public double ArrowKeyDistance
        {
            get { return _arrowKeyDistance; }
            set { _arrowKeyDistance = value; }
        }

        private void ZoomTo(Point3D targetPoint, BoardsViewModel.BoardTypes targetTypes,
            double zoomHeight = 0)
        {
            if (zoomHeight == 0)
            {
                zoomHeight = 25;
            }
            var zoomSize = new Size3D(0, 0, zoomHeight);

            foreach (GameBoardVisual cur in _gameBoardVisuals)
            {
                if (targetTypes == BoardsViewModel.BoardTypes.All
                    || targetTypes == cur.BoardType)
                {
                    cur.ZoomTo(new Rect3D(targetPoint, zoomSize), 500);
                }
            }
        }


        public void RemoveGrid()
        {
            if (_visualGridFactory != null)
            {
                foreach (GameBoardVisual cur in _gameBoardVisuals)
                {
                    cur.RemoveVisual(_visualGridFactory.FactoryVisualID);
                }
            }
        }


        internal void HandleTapeMeasurerHit(Point3D hit)
        {
            //MessageBox.Show(hit.ToString());
            _tapeMeasurerControl.HandleHit(hit);
            if (_tapeMeasurerControl.TapeMeasurerDrawMode == TapeMeasurerControl.DrawModeTapeMeasurer.StartOnly)
            {
                foreach (GameBoardVisual cur in _gameBoardVisuals)
                {
                    cur.AddVisual(_tapeMeasurerControl.StartPointFactory.FactoryVisualID,
                        _tapeMeasurerControl.StartPointFactory.MakeVisual());
                }
            }
            else if (_tapeMeasurerControl.TapeMeasurerDrawMode == TapeMeasurerControl.DrawModeTapeMeasurer.End)
            {
                foreach (GameBoardVisual cur in _gameBoardVisuals)
                {
                    cur.AddVisual(_tapeMeasurerControl.EndPointFactory.FactoryVisualID,
                        _tapeMeasurerControl.EndPointFactory.MakeVisual());
                    cur.AddVisual(_tapeMeasurerControl.TextVisualFactory.FactoryVisualID,
                        _tapeMeasurerControl.TextVisualFactory.MakeVisual());
                    cur.AddVisual(_tapeMeasurerControl.DistanceIndicatorFactory.FactoryVisualID,
                        _tapeMeasurerControl.DistanceIndicatorFactory.MakeVisual());
                }
            }
        }


        public void OpenVisuals(Dictionary<Guid, MoveablePicture> readCharacters)
        {
            foreach (var curPiece in readCharacters)
            {
                RegisterMoveablePicture(curPiece.Value, curPiece.Key);
            }
        }

        public void OpenBoardInfo(BoardInfo savedinfo)
        {
            SetBoardBackground(savedinfo.BoardImageName, 
                savedinfo.DefinedBoardHeight, 
                savedinfo.DefinedBoardWidth, 
                savedinfo.BoardHillValue,
                savedinfo.MaintainRatio,
                savedinfo.NoisePermutations);
        }

        public void RegisterGameBoardVisual(GameBoardVisual gameBoardVisual)
        {
            _gameBoardVisuals.Add(gameBoardVisual);
            if (gameBoardVisual.BoardType == BoardsViewModel.BoardTypes.Main)
            {
                MainCamera = gameBoardVisual.Camera;
            }
            PopulatedGameBoardWithVisuals(gameBoardVisual);
        }

        private void PopulatedGameBoardWithVisuals(GameBoardVisual gameBoardVisual)
        {
            foreach (MoveablePicture curPiece in _charactersToMoveablePicture.Values)
            {
                if (curPiece.BoardsInvisibleTo.Contains(gameBoardVisual.BoardType))
                {
                    continue;
                }
                gameBoardVisual.AddVisual(curPiece.TheCharImageFactory.FactoryVisualID,
                    curPiece.TheCharImageFactory.MakeVisual());
                gameBoardVisual.AddVisual(curPiece.TheBaseConeFactory.FactoryVisualID,
                    curPiece.TheBaseConeFactory.MakeVisual());
            }
            if (TerrainViewModel.TheMapFactory != null)
            {
                gameBoardVisual.AddVisual(TerrainViewModel.TheMapFactory.FactoryVisualID, TerrainViewModel.TheMapFactory.MakeVisual());
            }
            _terrainViewModel.PopulateBoardWithTerrain(gameBoardVisual);
        }

        internal bool HasAssociatedVisual(Guid characterGuid)
        {
            return _charactersToMoveablePicture.ContainsKey(characterGuid);
        }

        public MoveablePicture MatchingVisual(Guid characterGuid)
        {
            return _charactersToMoveablePicture[characterGuid];
        }

        public void RemakeInfoTextFor(Guid guid, List<StatusEffectDisplay> statuses)
        {
            _charactersToMoveablePicture[guid].RemakeInfoText(statuses);
        }

        public void ZoomTo(List<Guid> visualIDs, BoardsViewModel.BoardTypes targetTypes)
        {
            foreach (GameBoardVisual cur in _gameBoardVisuals)
            {
                if (targetTypes != BoardsViewModel.BoardTypes.All &&
                    cur.BoardType != targetTypes)
                {
                    continue;
                }
                var matchingPictures = new List<MoveablePicture>();
                foreach (Guid curID in visualIDs)
                {
                    MoveablePicture matchingChar = _charactersToMoveablePicture[curID];
                    if (!matchingChar.BoardsInvisibleTo.Contains(cur.BoardType))
                    {
                        matchingPictures.Add(matchingChar);
                    }
                }
                if (matchingPictures.Any())
                {
                    Point3D midpoint = Helper3DCalcs.FindMidpoint(matchingPictures.Select(x => x.Location));
                    ZoomTo(midpoint, targetTypes);
                }
            }
        }


        public void ZoomTo(Guid guid, BoardsViewModel.BoardTypes targetTypes)
        {
            ZoomTo(new List<Guid> {guid}, targetTypes);
        }

        internal void ZoomToPlacedTerrainVisual(Guid guid, BoardsViewModel.BoardTypes targetTypes)
        {
            BaseTerrainData matchingData = _terrainViewModel.GetTerrainDataFor(guid);
            ZoomTo(matchingData.CenterOfTerrainFeature, targetTypes, matchingData.ZoomHeight);
        }

        public void HighlightPlacedTerrainVisual(Guid guid, BoardsViewModel.BoardTypes targets)
        {
            _terrainViewModel.HighlightTerrain(guid, targets);
 
        }

      

        internal void OtherBoardPieceMoved(object sender, EventArgs e)
        {
            var movedEvent = e as PieceMovedEventsArg;
            MoveablePicture matchingVisual = _charactersToMoveablePicture[movedEvent.MoverID];
            matchingVisual.SetLocationTo(new Point3D(movedEvent.Destination.X, movedEvent.Destination.Y,
                movedEvent.Destination.Z));
        }


        public void ResetVisualDataFor(Guid UniqueCharacterID, string name, int speed, int height)
        {
            MoveablePicture matchingCharacter = CharactersToMoveablePicture[UniqueCharacterID];
            if (matchingCharacter != null)
            {
                var replacement = new MoveablePicture(matchingCharacter.PictureFileName, height, name,
                    matchingCharacter.PieceColor,
                    matchingCharacter.Location,
                    matchingCharacter.StatusEffects,
                    speed,
                    matchingCharacter.MovementCirclesRadiusList,
                    UniqueCharacterID);
                RemovePiece(UniqueCharacterID);
                RegisterMoveablePicture(replacement, UniqueCharacterID);
            }
        }

        public void MoveSelectedPiecesToFormation(TroopFormation selectedFormation)
        {
            List<MoveablePicture> selectedCharacters =
                CharactersToMoveablePicture.Where(x => x.Value.IsSelected).Select(z => z.Value).ToList();
            if (selectedCharacters.Count <= 1)
            {
                return;
            }
            Point3D averagePoint = Helper3DCalcs.FindMidpoint(selectedCharacters.Select(x => x.Location));
            switch (selectedFormation)
            {
                case TroopFormation.Circle:
                    MoveCharactersToCircleFormation(
                        _terrainViewModel.ShapeSizeValue, selectedCharacters, averagePoint);
                    break;
                case TroopFormation.Grid:
                    MoveCharactersToGridFormation(_terrainViewModel.ShapeSizeValue, selectedCharacters, averagePoint);
                    break;
                case TroopFormation.Random:
                    MoveCharactersToRandomFormation(_terrainViewModel.ShapeSizeValue, selectedCharacters, averagePoint);
                    break;
                case TroopFormation.Tight:
                    MoveCharactersToGridFormation(selectedCharacters.Max(x => x.LongestPictureSide), selectedCharacters,
                        averagePoint);
                    break;
            }
        }

        private void MoveCharactersToGridFormation(double distance, List<MoveablePicture> selectedCharacters,
            Point3D averagePoint)
        {
            double squareSize = Math.Ceiling(Math.Sqrt(selectedCharacters.Count));
            //We move the average up and to the left so we can lazily lay out the characters in the grid.
            double adjust = (squareSize/2)*distance;
            var movedAverage = new Point3D(averagePoint.X - adjust, averagePoint.Y - adjust, averagePoint.Z);
            int curRow = 0;
            int curColumn = 0;
            foreach (MoveablePicture curCharacter in selectedCharacters)
            {
                var newLocation = new Point3D(movedAverage.X + (curColumn*distance), movedAverage.Y + (curRow*distance),
                    curCharacter.Location.Z);
                MovePieceToPoint(curCharacter.UniqueID, newLocation, TimeSpan.FromSeconds(1), true);
                curColumn++;
                if (curColumn == squareSize)
                {
                    curRow++;
                    curColumn = 0;
                }
            }
        }

        private void MoveCharactersToRandomFormation(double distance, List<MoveablePicture> selectedCharacters,
            Point3D averagePoint)
        {
            double minDistanceFromOther = selectedCharacters.Max(x => x.LongestPictureSide);
            int maxAttemptsAtNoOverlap = 3;

            foreach (MoveablePicture curCharacter in selectedCharacters)
            {
                int curAttempstAtNoOverlap = 0;
                var newPosition = new Point3D();
                while (curAttempstAtNoOverlap < maxAttemptsAtNoOverlap)
                {
                    Vector3D curDirection = Helper3DCalcs.RandomVectorXY();
                    int randomdistance = _random.Next(1, (int) distance + 1);
                    newPosition = averagePoint + (curDirection*randomdistance);
                    bool anyTooClose = false;
                    foreach (MoveablePicture curOther in selectedCharacters)
                    {
                        if (curCharacter == curOther)
                        {
                            continue;
                        }
                        if ((curOther.Location - newPosition).Length < minDistanceFromOther)
                        {
                            anyTooClose = true;
                            break;
                        }
                    }
                    if (!anyTooClose)
                    {
                        break;
                    }
                    curAttempstAtNoOverlap++;
                }
                MovePieceToPoint(curCharacter.UniqueID, newPosition, TimeSpan.FromSeconds(1), true);
            }
        }

        private void MoveCharactersToCircleFormation(double distance, List<MoveablePicture> selectedCharacters,
            Point3D averagePoint)
        {
            double pointsOnCircle = selectedCharacters.Count;
            double circleSections = 360/pointsOnCircle;
            List<Point3D> newLocations = Helper3DCalcs.CirclePoints(distance, averagePoint, circleSections);
            for (int curIndex = 0; curIndex < selectedCharacters.Count; curIndex++)
            {
                MoveablePicture curchar = selectedCharacters[curIndex];
                Point3D newPosition = newLocations[curIndex];
                MovePieceToPoint(curchar.UniqueID, newPosition, TimeSpan.FromSeconds(1), true);
            }
        }


        public void ShowMapExpansion(bool showExpansion)
        {
            if (showExpansion)
            {
                var data = new MapExentionVisualData
                {
                    Origin = new Point3D(0, 0, -2),
                    Length = CurrentBoardInfo.DefinedBoardWidth*100,
                    MaterialColor = Colors.Gray,
                    Width = CurrentBoardInfo.DefinedBoardHeight*100,
                };
                TerrainViewModel.ExtraMapSpace = new MapExtensionVisualFactory(data);
                foreach (GameBoardVisual cur in _gameBoardVisuals)
                {
                    cur.AddVisual(TerrainViewModel.ExtraMapSpace.FactoryVisualID, TerrainViewModel.ExtraMapSpace.MakeVisual());
                }
            }
            else
            {
                foreach (GameBoardVisual cur in _gameBoardVisuals)
                {
                    cur.RemoveVisual(TerrainViewModel.ExtraMapSpace.FactoryVisualID);
                }
                TerrainViewModel.ExtraMapSpace = null;
            }
        }


        internal void ToggleCharacterInvisible(List<Guid> targetCharacters,
            BoardsViewModel.BoardTypes theType)
        {
            foreach (Guid curID in targetCharacters)
            {
                MoveablePicture curChar = _charactersToMoveablePicture[curID];
                if (!curChar.BoardsInvisibleTo.Contains(theType))
                {
                    MakeInvisible(curChar, theType);
                }
                else
                {
                    MakeNotInvisible(curChar, theType);
                }
            }
        }

        public void SetCharactersToVisibleStatus(Guid targetCharacter, BoardsViewModel.BoardTypes theType,
                                                 VisiblityType visiblityType)
        {
            MoveablePicture curChar = _charactersToMoveablePicture[targetCharacter];
            foreach (GameBoardVisual curBoard in _gameBoardVisuals.Where(x => x.BoardType == theType))
            {
                var relevantVisuals = curChar.CanBeTranslucent();
                curBoard.SetVisualOpacityTo(relevantVisuals, visiblityType);
            }

        }

        private void MakeNotInvisible(MoveablePicture curChar, BoardsViewModel.BoardTypes theType)
        {
            foreach (GameBoardVisual curBoard in _gameBoardVisuals.Where(x => x.BoardType == theType))
            {
                curBoard.AddVisual(curChar.TheCharImageFactory.FactoryVisualID,
                    curChar.TheCharImageFactory.MakeVisual());
                curBoard.AddVisual(curChar.TheBaseConeFactory.FactoryVisualID,
                    curChar.TheBaseConeFactory.MakeVisual());
            }
            curChar.MarkNotInvisibleOn(theType);
        }

        private void MakeInvisible(MoveablePicture curChar, BoardsViewModel.BoardTypes theType)
        {
            foreach (GameBoardVisual curBoard in _gameBoardVisuals.Where(x => x.BoardType == theType))
            {
                foreach (MovementCircleVisualFactory cur in curChar.MovementCirclesFactories)
                {
                    curBoard.RemoveVisual(cur.FactoryVisualID);
                }
                curBoard.RemoveVisual(curChar.TheBaseConeFactory.FactoryVisualID);
                curBoard.RemoveVisual(curChar.TheCharImageFactory.FactoryVisualID);
                curBoard.RemoveVisual(curChar.TheInfoTextFactory.FactoryVisualID);
            }
            curChar.MarkInvisibleOn(theType);
        }


        internal void RemoveVisuals(Guid guid)
        {
            foreach (GameBoardVisual cur in _gameBoardVisuals)
            {
                cur.RemoveVisual(guid);
            }
        }

        public void AddVisuals(BaseVisualFactory theFactory)
        {
            foreach (GameBoardVisual cur in _gameBoardVisuals)
            {
                cur.AddVisual(theFactory.FactoryVisualID, theFactory.MakeVisual());
            }

        }


     
        private void MainCameraViewChanged(object sender, EventArgs e)
        {
            if (!OrientAllCamerasToMatchMain)
            {
                return;
            }
            
            Instance.SetCamerasToMatchMain();
        }


        public void SetCamerasToMatchMain()
        {
            GameBoardVisual mainBoard =
                _gameBoardVisuals.FirstOrDefault(x => x.BoardType == BoardsViewModel.BoardTypes.Main);
            foreach (GameBoardVisual cur in _gameBoardVisuals.Where(x => x.BoardType != BoardsViewModel.BoardTypes.Main)
                )
            {
                //Don't match if we've got all invisible characters selected.
                if (_charactersToMoveablePicture.Where(x => x.Value.IsSelected)
                                                .All(x => x.Value.BoardsInvisibleTo.Contains(cur.BoardType)))
                {
                    continue;
                }
                cur.MatchOtherCamera(mainBoard.Camera);
            }
        }

        internal void ClearViewModel()
        {
            foreach (Guid curKey in _charactersToMoveablePicture.Keys.ToList())
            {
                RemovePiece(curKey);
            }
            _gameBoardVisuals.Clear();
            _terrainViewModel.ClearViewModel();
            MainCamera = null;
        }


        internal void RemovePlacedTerrainVisual(BaseTerrainData terrainData)
        {
            RemoveVisuals(terrainData.FactoryID);
            _terrainViewModel.RemoveTerrainVisualData(terrainData.FactoryID);
        }

        internal void SetMouseCursorToAoEFor(TerrainViewModel.ShapeMode value)
        {
            foreach (GameBoardVisual cur in _gameBoardVisuals)
            {
                cur.SetMouseCursorToAoEFor(value);
            }
        }

        internal void OpenTerrain(TerrainViewModel terrainViewModel)
        {
            _terrainViewModel.PopulateFromViewModel(terrainViewModel);
        }

        public void ChangeElevationBy(Guid guid, double heightChangeValue)
        {
            var pointHack = new Point3D(_charactersToMoveablePicture[guid].Location.X,
                _charactersToMoveablePicture[guid].Location.Y,
                _charactersToMoveablePicture[guid].Location.Z -
                _charactersToMoveablePicture[guid].FlightHeight);
            _charactersToMoveablePicture[guid].FlightHeight += heightChangeValue;
            MovePieceToPoint(guid, pointHack, TimeSpan.FromSeconds(1));
        }

        public void SetElevationTo(Guid guid, double targetHeightValue)
        {
            ChangeElevationBy(guid, targetHeightValue - _charactersToMoveablePicture[guid].FlightHeight);
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string name)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(name));
            }
        }

        #endregion

        public void SetHillsToLevel(double newHillValue)
        {
            SetBoardBackground(
                CurrentBoardInfo.BoardImageName, 
                CurrentBoardInfo.DefinedBoardHeight, 
                CurrentBoardInfo.DefinedBoardWidth, 
                newHillValue, 
                CurrentBoardInfo.MaintainRatio,
                CurrentBoardInfo.NoisePermutations);
        }

        public void ChangeBoardSeed()
        {
            SetBoardBackground(
                CurrentBoardInfo.BoardImageName,
                CurrentBoardInfo.DefinedBoardHeight,
                CurrentBoardInfo.DefinedBoardWidth,
                CurrentBoardInfo.BoardHillValue,
                CurrentBoardInfo.MaintainRatio,
                null);
        }

        public void SetBoardBackground(string targetFile, double definedBoardHeight, double definedBoardWidth, double hillHeight, bool maintainRatio)
        {
            byte[] noiseInfo = null;
            if (CurrentBoardInfo != null)
            {
                noiseInfo = CurrentBoardInfo.NoisePermutations;   
            }
            SetBoardBackground(targetFile, definedBoardHeight, definedBoardWidth, hillHeight, maintainRatio, noiseInfo);
        }


        public double GetElevationOf(Guid uniqueCharacterId)
        {
            return _charactersToMoveablePicture[uniqueCharacterId].FlightHeight;
        }


        public void MovePiecesInDirection(KeyToDirection.MovementDirection desiredDirection)
        {
            var selectedPieces = _charactersToMoveablePicture.Where(x => x.Value.IsSelected).ToList();
            if (!selectedPieces.Any())
            {
                return;
            }
            var positionAdjustment = KeyToDirection.VectorFromDirection(desiredDirection)*ArrowKeyDistance;
            foreach (var cur in selectedPieces)
            {
                var newPos = cur.Value.Location + positionAdjustment;
                MovePieceToPoint(cur.Value.UniqueID, newPos, TimeSpan.FromSeconds(.3), true);
            }
        }
    }
}